#include "KVStateMachine.h"

#include "KVCommand.h"


KVStateMachine::KVStateMachine() : skip_list_(18) {}

bool KVStateMachine::apply(std::shared_ptr<raft::utils::Command> &command, std::string *msg) {
    if (!command) {
        *msg = "Invalid log entry.";
        return false;
    }

    if (auto *kv_command = dynamic_cast<const KVCommand *>(command.get())) {
        switch (kv_command->get_command()) {
            case KVCommand::Command::INSERT:
                return insert_element(kv_command->get_key(), kv_command->get_value(), msg);
            case KVCommand::Command::SET:
                return set_element(kv_command->get_key(), kv_command->get_value());
            case KVCommand::Command::DELETE:
                return delete_element(kv_command->get_key(), msg);
            case KVCommand::Command::SEARCH:
                return search_element(kv_command->get_key(), msg);
            default:
                *msg = "Unknown command.";
                return false;
        }
    } else {
        *msg = "Invalid log entry type.";
        return false;
    }
}

grpc::Status KVStateMachine::execute_command(grpc::ServerContext *context,
                                             const middleware_rpc_proto::ExecuteCommand *request,
                                             middleware_rpc_proto::ExecuteCommandReply *reply) {
    const auto &entry = request->apply_msg();
    KVCommand kv_command;
    kv_command.parse_from_string(entry);
    std::string value;
    bool result = search_element(kv_command.get_key(), &value);
    reply->set_is_success(true);
    reply->set_result(value);
    return grpc::Status::OK;
}

std::string KVStateMachine::snapshot() {
    return skip_list_.dump_file();
}

void KVStateMachine::restore(const std::string &snapshot) {
    skip_list_.load_file(snapshot);
}

bool KVStateMachine::insert_element(const std::string &key, const std::string &value, std::string *msg) {
    if (!skip_list_.insert_element(key, value)) {
        *msg = "The element to be inserted already exists.";
        return false;
    }
    return true;
}

bool KVStateMachine::delete_element(const std::string &key, std::string *msg) {
    if (!skip_list_.delete_element(key)) {
        *msg = "The element to be deleted does not exist.";
        return false;
    }
    return true;
}

bool KVStateMachine::search_element(const std::string &key, std::string *msg) {
    std::string value;
    if (!skip_list_.search_element(key, &value)) {
        *msg = "The element does not exist.";
        return false;
    }
    *msg = value;
    return true;
}

bool KVStateMachine::set_element(const std::string &key, const std::string &value) {
    skip_list_.set_element(key, value);
    return true;
}